#include <vector>
#include <iostream>
#include <string>
#include <string>
#include <regex>
#include <map>

void regex_match_test();
void regex_search_test();
void regex_replace_test();

template <typename T, typename... Ts>
auto printf3(T value, Ts... args)
{
    std::cout << value << std::endl;
    (void)std::initializer_list<T>{([&args]
                                    { std::cout << args << std::endl; }(),
                                    value)...};
}

template <typename T, typename U>
auto add(T t, U u)
{
    return t + u;
}

template <typename... T>
auto average(T... t)
{
    return (t + ...) / sizeof...(t);
}

template <typename Key, typename Value, typename F>
void update(std::map<Key, Value> &m, F foo)
{
    for (auto &&[key, value] : m)
        value = foo(key);
}

int main()
{
    // printf3("12321oooo", 'a', 333, "lll");
    // auto re = add(32, 'A');
    // std::cout << re << std::endl;
    // average(11, 321, 43, 5, 43634, 63);

    // std::map<std::string, long long> m{
    //     {"a", 1},
    //     {"b", 2},
    //     {"c", 3}};
    // update(m, [](std::string key) -> long long
    //        { return std::hash<std::string>{}(key); });
    // for (auto &&[key, value] : m)
    //     std::cout << key << ":" << value << std::endl;

    regex_match_test();
    regex_search_test();
    regex_replace_test();
    return 0;
}

void regex_match_test()
{
    // regex_match example

    if (std::regex_match("subject", std::regex("(sub)(.*)")))
        std::cout << "string literal matched\n";

    std::string s("subject");
    std::regex e("(sub)(.*)"); // Regular expression
    if (std::regex_match(s, e))
        std::cout << "string object matched\n";

    if (std::regex_match(s.begin(), s.end(), e))
        std::cout << "range matched\n";

    std::cmatch cm; // same as std::match_results<const char*> cm;
    std::regex_match("subject", cm, e);
    std::cout << "string literal with " << cm.size() << " matches\n";

    std::smatch sm; // same as std::match_results<string::const_iterator> sm;
    std::regex_match(s, sm, e);
    std::cout << "string object with " << sm.size() << " matches\n";

    std::regex_match(s.cbegin(), s.cend(), sm, e);
    std::cout << "range with " << sm.size() << " matches\n";

    // using explicit flags:
    std::regex_match("subject", cm, e, std::regex_constants::match_default);

    std::cout << "the matches were: ";
    for (unsigned i = 0; i < sm.size(); ++i)
    {
        std::cout << "[" << sm[i] << "] ";
    }

    std::cout << std::endl;
}

void regex_search_test()
{
    std::string s("this subject has a submarine as a subsequence");
    std::smatch m;
    std::regex e("\\b(sub)([^ ]*)"); // matches words beginning by "sub"

    std::cout << "Target sequence: " << s << std::endl;
    std::cout << "Regular expression: /\\b(sub)([^ ]*)/" << std::endl;
    std::cout << "The following matches and submatches were found:" << std::endl;

    while (std::regex_search(s, m, e))
    {
        for (auto x = m.begin(); x != m.end(); x++)
            std::cout << x->str() << " ";
        std::cout << "--> ([^ ]*) match " << m.format("$2") << std::endl;
        s = m.suffix().str();
    }
}

void regex_replace_test()
{
    char buf[20];
    const char *first = "axayaz";
    const char *last = first + strlen(first);
    std::regex rx("a"); // Regular expression
    std::string fmt("A");
    std::regex_constants::match_flag_type fonly =
        std::regex_constants::format_first_only;

    *std::regex_replace(&buf[0], first, last, rx, fmt) = '\0';
    std::cout << &buf[0] << std::endl;

    *std::regex_replace(&buf[0], first, last, rx, fmt, fonly) = '\0';
    std::cout << &buf[0] << std::endl;

    std::string str("adaeaf");
    std::cout << std::regex_replace(str, rx, fmt) << std::endl;
    std::cout << std::regex_replace(str, rx, fmt, fonly) << std::endl;
}